package org.hxzon.cyclone.pcontacts;

import java.util.List;

import org.hxzon.cyclone.Util;
import org.hxzon.cyclone.Vector3;

/**
 * The contact resolution routine for particle contacts. One
 * resolver instance can be shared for the whole simulation.
 */
//碰撞处理器
public class ParticleContactResolver {
    //附加，限制处理次数
    protected int iterations;

    //附加，记录当前已处理的次数
    protected int iterationsUsed;

    public ParticleContactResolver(int iterations) {
        this.iterations = iterations;
    }

    public void setIterations(int iterations) {
        this.iterations = iterations;
    }

    /**
     * Resolves a set of particle contacts for both penetration
     * and velocity.
     *
     * Contacts that cannot interact with each other should be
     * passed to separate calls to resolveContacts, as the
     * resolution algorithm takes much longer for lots of contacts
     * than it does for the same number of contacts in small sets.
     *
     * @param contacts Pointer to an array of particle contact
     * objects.
     *
     * @param numContacts The number of contacts in the array to
     * resolve.
     *
     * @param numIterations The number of iterations through the
     * resolution algorithm. This should be at least the number of
     * contacts (otherwise some constraints will not be resolved -
     * although sometimes this is not noticable). If the
     * iterations are not needed they will not be used, so adding
     * more iterations may not make any difference. But in some
     * cases you would need millions of iterations. Think about
     * the number of iterations as a bound: if you specify a large
     * number, sometimes the algorithm WILL use it, and you may
     * drop frames.
     *
     * @param duration The duration of the previous integration step.
     * This is used to compensate for forces applied.
    */
    public void resolveContacts(List<ParticleContact> contacts, double duration) {

        iterationsUsed = 0;
        while (iterationsUsed < iterations) {
            // Find the contact with the largest closing velocity;
            double max = Util.RealMax;
            int maxIndex = contacts.size();
            for (int i = 0; i < contacts.size(); i++) {
                double sepVel = contacts.get(i).calculateSeparatingVelocity();
                if (sepVel < max && (sepVel < 0 || contacts.get(i).penetration > 0)) {
                    max = sepVel;
                    maxIndex = i;
                }
            }

            // Do we have anything worth resolving?
            if (maxIndex == contacts.size()) {
                break;
            }

            // Resolve this contact
            contacts.get(maxIndex).resolve(duration);

            // Update the interpenetrations for all particles
            Vector3[] move = contacts.get(maxIndex).particleMovement;
            for (int i = 0; i < contacts.size(); i++) {
                if (contacts.get(i).particle[0] == contacts.get(maxIndex).particle[0]) {
                    contacts.get(i).penetration -= move[0].scalarProduct(contacts.get(i).contactNormal);//v*v
                } else if (contacts.get(i).particle[0] == contacts.get(maxIndex).particle[1]) {
                    contacts.get(i).penetration -= move[1].scalarProduct(contacts.get(i).contactNormal);
                }
                if (contacts.get(i).particle[1] != null) {
                    if (contacts.get(i).particle[1] == contacts.get(maxIndex).particle[0]) {
                        contacts.get(i).penetration += move[0].scalarProduct(contacts.get(i).contactNormal);
                    } else if (contacts.get(i).particle[1] == contacts.get(maxIndex).particle[1]) {
                        contacts.get(i).penetration += move[1].scalarProduct(contacts.get(i).contactNormal);
                    }
                }
            }

            iterationsUsed++;
        }
    }
}
